package com.ty.user.starter.exception;

import com.ty.user.starter.constants.Constant;
import com.ty.user.starter.util.R;
import com.ty.user.starter.util.ResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.validation.ValidationException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.text.ParseException;
import java.util.NoSuchElementException;


/**
 * 全局异常处理
 *
 * @author liuxiaomin
 */
@Slf4j
@RestControllerAdvice
@ResponseBody
public class CommonExceptionAdvice {

    public static final String WARM_PROMPT = "温馨提示：";
    public static final String CODE_1451 = "温馨提示：该数据已产生业务行为，不能删除";
    public static final String CLIENT_ABORT_MSG = "客户端终止异常";

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public R<String> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        log.error("缺少请求参数", e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, "缺少请求参数");
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public R<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error("参数解析失败", e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, "参数解析失败：could_not_read_json");
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(IllegalArgumentException.class)
    public R<String> handleIllegalArgumentException(IllegalArgumentException e) {
        log.error("非法参数异常", e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, e.getMessage());
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error("参数验证失败", e);
        BindingResult result = e.getBindingResult();
        FieldError error = result.getFieldError();
        assert error != null;
        String field = error.getField();
        String code = error.getDefaultMessage();
        String message = String.format("%s:%s", field, code);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, message);
    }

    /**
     * 方法参数类型不匹配异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public R<String> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        log.error("方法参数类型", e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, "缺少参数或参数类型不匹配");
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public R<String> handleBindException(BindException e) {
        log.error("参数绑定失败", e);
        BindingResult result = e.getBindingResult();
        FieldError error = result.getFieldError();
        assert error != null;
        String code = error.getDefaultMessage();
        String message = String.format("%s：%s", "温馨提示", code);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, message);
    }


    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ValidationException.class)
    public R<String> handleValidationException(ValidationException e) {
        log.error("参数验证失败", e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, "validation_exception");
    }

    /**
     * 405 - Method Not Allowed
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<String> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        log.error("不支持当前请求方法", e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, "不支持当前请求方法");
    }

    /**
     * 415 - Unsupported Media Type
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public R<String> handleHttpMediaTypeNotSupportedException(Exception e) {
        log.error("不支持当前媒体类型", e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, "不支持的媒体类型");
    }


    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(ParseException.class)
    public R<String> handleParseException(ParseException e) {
        log.error("数据转换错误", e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, "数据转换错误：" + e.getMessage());
    }


    /**
     * 文件大小
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public R<String> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
        log.error("上传文件超出限制:", e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, "上传文件大小不能超过" + 20);
    }

    /**
     * HTTP 5xx is received.
     */
    @ResponseStatus(HttpStatus.BAD_GATEWAY)
    @ExceptionHandler(HttpServerErrorException.class)
    public R<String> handleHttpServerErrorException(HttpServerErrorException e) {
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, e.getMessage());
    }


    /**
     * @param e 异常
     * @return 返回信息
     */
    @ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
    @ExceptionHandler(NoSuchElementException.class)
    public R<String> handleNoSuchElementException(NoSuchElementException e) {
        log.error("文件格式错误", e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, WARM_PROMPT + "上传文件不可读，请确认格式是否正确");
    }


    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
    public R<String> handleHttpMediaTypeNotAcceptableException(HttpMediaTypeNotAcceptableException e) {
        log.error("Http媒体类型不可接受的异常", e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, "Http媒体类型不可接受的异常");
    }

    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(ClientAbortException.class)
    public R<String> handleClientAbortException(ClientAbortException e) {
        log.error(CLIENT_ABORT_MSG, e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, CLIENT_ABORT_MSG);
    }

    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> handleSQLIntegrityConstraintViolationException(SQLIntegrityConstraintViolationException e) {
        log.error("数据完整性破坏异常:", e);
        int errCode = e.getErrorCode();
        if (errCode == 1451) {
            return ResponseUtil.failed(Constant.CODE_EXCEPTION, CODE_1451);
        } else if (errCode == 1048) {
            return ResponseUtil.failed(Constant.CODE_EXCEPTION, WARM_PROMPT + "必填信息不能为空，请确认");
        } else if (errCode == 1062) {
            return ResponseUtil.failed(Constant.CODE_EXCEPTION, WARM_PROMPT + "信息已存在，请勿重复添加");
        }
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, "数据库操作异常");
    }

    /**
     * 500
     * 参数异常
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(UserMgtException.class)
    public R<String> handleUserMgtException(UserMgtException e) {
        log.error(CLIENT_ABORT_MSG, e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, e.getMessage());
    }

    /**
     * 500
     * shiro密码错误异常
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(IncorrectCredentialsException.class)
    public R<String> handleIncorrectCredentialsException(IncorrectCredentialsException e) {
        log.error(CLIENT_ABORT_MSG, e);
        return ResponseUtil.failed(Constant.CODE_EXCEPTION, "密码错误");
    }
}
